package com.zebra.gateway;

import cn.hutool.core.thread.NamedThreadFactory;
import com.zebra.common.concurrent.ZebraExecutor;
import com.zebra.common.concurrent.ZebraExecutorGroup;
import com.zebra.gateway.processor.GatewayProcessor;
import com.zebra.remoting.AbstractRemotingServer;
import com.zebra.remoting.config.ConfigManager;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.util.concurrent.*;

/**
 * Manager of processors<br>
 * Maintains the relationship between command and command processor through command code.
 * @author sk
 */
public class GwClientProcessorManager {
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(GwClientProcessorManager.class);

    private static GwClientProcessorManager processorManager;
    private ConcurrentHashMap<Integer, GatewayProcessor> gatewayProcessors = new ConcurrentHashMap<>(4);
    private GatewayProcessor defaultProcessor;

    /** The default executor, if no executor is set for processor, this one will be used */
    private ExecutorService                                      defaultExecutor;

    private ZebraExecutorGroup executorGroup;

    private int                                                  cpuSize    = ConfigManager
                                                                                    .default_ep_size();

    private int                                                  minPoolSize    = ConfigManager
                                                                                    .default_tp_min_size();

    private int                                                  maxPoolSize    = ConfigManager
                                                                                    .default_tp_max_size();

    private int                                                  queueSize      = ConfigManager
                                                                                    .default_tp_queue_size();

    private long                                                 keepAliveTime  = ConfigManager
                                                                                    .default_tp_keepalive_time();

    public static GwClientProcessorManager getInstance() {
        if (null == processorManager) {
            processorManager = new GwClientProcessorManager();
        }
        return processorManager;
    }

    private GwClientProcessorManager() {
    }

    public void start(int disSize, int poolSize){
        defaultExecutor = new ThreadPoolExecutor(poolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(queueSize),
                new NamedThreadFactory("c-h-exe", true));

        int num = Runtime.getRuntime().availableProcessors()  * disSize;
        num = num < 64 ? 64 : num;
        logger.warn("Initialize client dis executorGroup size {}", num);
        executorGroup = new ZebraExecutorGroup(disSize, "c-h-dis");
        executorGroup.start();
    }


    public ZebraExecutor getDisruptorByLong(long userId){
        int index = (int) (userId % executorGroup.size());
        return executorGroup.getByIndex(index);
    }

    public ZebraExecutor getDisruptor(int no){
        return executorGroup.getByBizNo(no);
    }


    /**
     * Register processor to process command that has the command code of cmdCode.
     * 
     * @param cmdCode
     * @param processor
     */
    public void registerProcessor(Integer cmdCode, GatewayProcessor  processor) {
        if (this.gatewayProcessors.containsKey(cmdCode)) {
            logger
                .warn(
                    "Processor for cmd={} is already registered, the processor is {}, and changed to {}",
                    cmdCode, gatewayProcessors.get(cmdCode).getClass().getName(), processor.getClass()
                        .getName());
        }
        this.gatewayProcessors.put(cmdCode, processor);
    }

    /**
     * Register the default processor to process command with no specific processor registered.
     * 
     * @param processor
     */
    public void registerDefaultProcessor(GatewayProcessor  processor) {
        if (this.defaultProcessor == null) {
            this.defaultProcessor = processor;
        } else {
            throw new IllegalStateException("The defaultProcessor has already been registered: "
                                            + this.defaultProcessor.getClass());
        }
    }

    /**
     * Get the specific processor with command code of cmdCode if registered, otherwise the default processor is returned.
     * 
     * @param cmdCode
     * @return
     */
    public GatewayProcessor  getProcessor(Integer cmdCode) {
        GatewayProcessor  processor = this.gatewayProcessors.get(cmdCode);
        return processor != null ? processor : this.defaultProcessor;
    }

    /**
     * Getter method for property <tt>defaultExecutor</tt>.
     * 
     * @return property value of defaultExecutor
     */
    public ExecutorService getDefaultExecutor() {
        return defaultExecutor;
    }

    /**
     * Set the default executor.
     * 
     * @param executor
     */
    public void registerDefaultExecutor(ExecutorService executor) {
        this.defaultExecutor = executor;
    }

}